Ensure we don't get duplicate '.' which leads to corruption
authorColin Walters <walters@verbum.org>
Sat, 15 Oct 2011 17:04:50 +0000 (13:04 -0400)
committerColin Walters <walters@verbum.org>
Sat, 15 Oct 2011 17:04:50 +0000 (13:04 -0400)
In path_split(), we squash '.' entirely, since it's redundant and if
we don't, we return an extra component which callers would then have
to handle.

In repo, ensure we're never explicitly parsing '.' either (yet).

src/libhacktree/hacktree-repo.c
src/libhtutil/ht-unix-utils.c

index b00655799e77d53223ac8c982d88336b8ad5748a..d80a31d0a74b64261b20eec700b28e3c1edceab7 100644 (file)
@@ -25,6 +25,7 @@
 #include "htutil.h"
 
 #include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
 
 static gboolean
 link_one_file (HacktreeRepo *self, const char *path,
@@ -875,6 +876,13 @@ check_path (const char *filename,
       goto out;
     }
 
+  if (strcmp (filename, ".") == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+  
   if (ht_util_filename_has_dotdot (filename))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -1117,6 +1125,8 @@ add_one_path_to_tree_and_import (HacktreeRepo   *self,
       file_sha1 = g_hash_table_lookup (current_tree->files, component);
       dir = g_hash_table_lookup (current_tree->directories, component);
 
+      g_assert_cmpstr (component, !=, ".");
+
       if (i < components->len - 1)
         {
           if (file_sha1 != NULL)
@@ -1195,6 +1205,56 @@ add_files_to_tree_and_import (HacktreeRepo   *self,
   return ret;
 }
 
+static gboolean
+commit_parsed_tree (HacktreeRepo *self,
+                    const char   *subject,
+                    const char   *body,
+                    GVariant     *metadata,
+                    ParsedTreeData *tree,
+                    GChecksum   **out_commit,
+                    GError      **error)
+{
+  HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GChecksum *root_checksum = NULL;
+  GChecksum *ret_commit = NULL;
+  GVariant *commit = NULL;
+  GDateTime *now = NULL;
+
+  if (!import_parsed_tree (self, tree, &root_checksum, error))
+    goto out;
+
+  now = g_date_time_new_now_utc ();
+  commit = g_variant_new ("(u@a{sv}sssts)",
+                          HACKTREE_COMMIT_VERSION,
+                          create_empty_gvariant_dict (),
+                          priv->current_head ? priv->current_head : "",
+                          subject, body ? body : "",
+                          g_date_time_to_unix (now) / G_TIME_SPAN_SECOND,
+                          g_checksum_get_string (root_checksum));
+  if (!import_gvariant_object (self, HACKTREE_SERIALIZED_COMMIT_VARIANT,
+                               commit, &ret_commit, error))
+    goto out;
+
+  if (!write_checksum_file (priv->head_ref_path, g_checksum_get_string (ret_commit), error))
+    goto out;
+
+  g_free (priv->current_head);
+  priv->current_head = g_strdup (g_checksum_get_string (ret_commit));
+
+  ret = TRUE;
+  *out_commit = ret_commit;
+ out:
+  if (root_checksum)
+    g_checksum_free (root_checksum);
+  if (commit)
+    g_variant_unref (commit);
+  if (now)
+    g_date_time_unref (now);
+  return ret;
+}
+
+
 gboolean
 hacktree_repo_commit (HacktreeRepo *self,
                       const char   *subject,
@@ -1210,10 +1270,7 @@ hacktree_repo_commit (HacktreeRepo *self,
   gboolean ret = FALSE;
   ParsedTreeData *tree = NULL;
   GVariant *previous_commit = NULL;
-  GVariant *commit = NULL;
-  GChecksum *root_checksum = NULL;
   GChecksum *ret_commit_checksum = NULL;
-  GDateTime *now = NULL;
 
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
   g_return_val_if_fail (priv->inited, FALSE);
@@ -1234,28 +1291,83 @@ hacktree_repo_commit (HacktreeRepo *self,
 
   if (!add_files_to_tree_and_import (self, base, modified_files, tree, error))
     goto out;
-  
-  if (!import_parsed_tree (self, tree, &root_checksum, error))
-    goto out;
 
-  now = g_date_time_new_now_utc ();
-  commit = g_variant_new ("(u@a{sv}sssts)",
-                          HACKTREE_COMMIT_VERSION,
-                          create_empty_gvariant_dict (),
-                          priv->current_head ? priv->current_head : "",
-                          subject, body ? body : "",
-                          g_date_time_to_unix (now) / G_TIME_SPAN_SECOND,
-                          g_checksum_get_string (root_checksum));
-  if (!import_gvariant_object (self, HACKTREE_SERIALIZED_COMMIT_VARIANT,
-                               commit, &ret_commit_checksum, error))
+  if (!commit_parsed_tree (self, subject, body, metadata, tree, &ret_commit_checksum, error))
     goto out;
+  
+  ret = TRUE;
+ out:
+  if (!ret)
+    {
+      if (ret_commit_checksum)
+        g_checksum_free (ret_commit_checksum);
+    }
+  else
+    {
+      *out_commit = ret_commit_checksum;
+    }
+  if (previous_commit)
+    g_variant_unref (previous_commit);
+  parsed_tree_data_free (tree);
+  return ret;
+}
 
-  if (!write_checksum_file (priv->head_ref_path, g_checksum_get_string (ret_commit_checksum), error))
-    goto out;
+gboolean      
+hacktree_repo_commit_from_filelist_fd (HacktreeRepo *self,
+                                       const char   *subject,
+                                       const char   *body,
+                                       GVariant     *metadata,
+                                       const char   *base,
+                                       int           fd,
+                                       char          separator,
+                                       GChecksum   **out_commit,
+                                       GError      **error)
+{
+  HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  ParsedTreeData *tree = NULL;
+  GVariant *previous_commit = NULL;
+  GChecksum *ret_commit_checksum = NULL;
+  GUnixInputStream *in = NULL;
+  GDataInputStream *datain = NULL;
+  char *filename = NULL;
+  gsize filename_len;
+  GError *temp_error = NULL;
 
-  g_free (priv->current_head);
-  priv->current_head = g_strdup (g_checksum_get_string (ret_commit_checksum));
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  /* We're overwriting the tree */
+  tree = parsed_tree_data_new ();
 
+  in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+  datain = g_data_input_stream_new ((GInputStream*)in);
+
+  while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+                                                    &filename_len, NULL, &temp_error)) != NULL)
+    {
+      if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+        {
+          if (temp_error != NULL)
+            {
+              g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+              goto out;
+            }
+        }
+      if (!add_one_path_to_tree_and_import (self, base, filename, tree, error))
+        goto out;
+      g_free (filename);
+      filename = NULL;
+    }
+  if (filename == NULL && temp_error != NULL)
+    {
+      g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+      goto out;
+    }
+  if (!commit_parsed_tree (self, subject, body, metadata,
+                           tree, &ret_commit_checksum, error))
+    goto out;
+  
   ret = TRUE;
  out:
   if (!ret)
@@ -1267,16 +1379,12 @@ hacktree_repo_commit (HacktreeRepo *self,
     {
       *out_commit = ret_commit_checksum;
     }
-  if (root_checksum)
-    g_checksum_free (root_checksum);
-  if (previous_commit)
-    g_variant_unref (previous_commit);
+  g_clear_object (&datain);
+  g_clear_object (&in);
+  g_free (filename);
   parsed_tree_data_free (tree);
-  if (commit)
-    g_variant_unref (commit);
-  if (now)
-    g_date_time_unref (now);
   return ret;
+  
 }
 
 static gboolean
@@ -1491,6 +1599,7 @@ checkout_one_directory (HacktreeRepo  *self,
   if (mkdir (dest_path, (mode_t)mode) < 0)
     {
       ht_util_set_error_from_errno (error, errno);
+      g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
       goto out;
     }
       
index 57aa1f421d810402868f6d7b9b0c0bfff7824e25..375c52a3fb85a56063dbfb73f87e0239a829c37d 100644 (file)
@@ -100,6 +100,7 @@ ht_util_path_split (const char *path)
   GPtrArray *ret = NULL;
   const char *p;
   const char *slash;
+  int i;
 
   g_return_val_if_fail (path[0] != '/', NULL);
 
@@ -121,6 +122,13 @@ ht_util_path_split (const char *path)
       }
   } while (p && *p);
 
+  /* Canonicalize by removing duplicate '.' */
+  for (i = ret->len-1; i >= 0; i--)
+    {
+      if (strcmp (ret->pdata[i], ".") == 0)
+        g_ptr_array_remove_index (ret, i);
+    }
+
   return ret;
 }